package com.zte.cl.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import com.zte.cl.handler.NewsBlackKeywordHandler;
import com.zte.cl.handler.NewsHandler;
import com.zte.cl.handler.NewsSimilarDegreeHandler;
import com.zte.cl.model.Img;
import com.zte.cl.model.News;
import com.zte.cl.repository.ImgRepository;
import com.zte.cl.repository.NewsRepository;
import com.zte.cl.service.CrawlerEHCacheUtil;
import com.zte.cl.service.NewsService;
import com.zte.cl.utils.ConstantUtil;
import com.zte.cl.utils.StringUtil;

@Service("newsService")
public class NewsServiceImpl implements NewsService {

	private static final Log log = LogFactory.getLog(NewsServiceImpl.class);
	
	@Resource
	private NewsRepository newsRepository;
	
	@Resource
	private ImgRepository imgRepository;
	
	@Override
	public News save(News news) {
		news = newsRepository.save(news);
		String pageUrl = news.getPageUrl();
		String title = news.getTitle();
		// 不在缓存中存放textPage(纯内容, 只有content)类型的news
		if(!StringUtil.isEmpty(pageUrl)|| !StringUtil.isEmpty(title)) {
			CrawlerEHCacheUtil.putCached(news.getChannelId(), pageUrl, title);
		}
		return news;
	}

	@Override
	public boolean isExistsByContent(String channelId, String content) {
		return newsRepository.countByChannelIdAndContent(channelId, content) > 0;
	}

	@Override
	public boolean isExistsByUrlOrTitle(String channelId, String pageUrl,
			String title) {
		// 先从缓存中判断
		boolean exists = CrawlerEHCacheUtil.isNewExistsByUrlOrTitle(channelId, pageUrl, title);
		if(!exists) {
			log.debug("缓存中不存在, 从数据库中查找!");
			if (!StringUtil.isEmpty(pageUrl)) {
				exists = newsRepository.countByChannelIdAndPageUrl(channelId, pageUrl) > 0;
			}
			if(!exists) {
				if (!StringUtil.isEmpty(title)) {
					exists = newsRepository.countByChannelIdAndTitle(channelId, title) > 0;
				}
			}
			
			// 如果从数据库中查询到记录，把该记录放入缓存
			if(exists) {
				log.debug("数据库中存在记录, 把此记录放入缓存");
				CrawlerEHCacheUtil.putCached(channelId, pageUrl, title);
			}
		} else {
			log.debug("缓存中存在channelId:" + channelId + ", pageUrl:" + pageUrl + ", title:" + title + "的news");
		}
		return exists;
	}

	/**
	 * 按照发布时间倒序取最新的5条新闻
	 */
	@Override
	public Map<String, String> buildPushNews() {
		List<News> newsList = newsRepository.findByIsEnabledTrueOrderByPublishTimeDesc();
		return buildPushNews(filterNews(newsList));
	}
	
	/**
	 * 按照关键词取出最新的5条新闻
	 */
	@Override
	public Map<String, String> buildPushNews(final String[] keywords) {
		if(keywords == null || keywords.length == 0) {
			return buildPushNews();
		}
		Specification<News> spec = new Specification<News>(){
			@Override
			public Predicate toPredicate(Root<News> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				// where条件,根据keyword字段和title字段进行过滤
				// keyword字段的where条件
				Predicate[] keywordArr = new Predicate[keywords.length];
				// title字段的where条件
				Predicate[] titleArr = new Predicate[keywords.length];
				for(int i = 0; i < keywords.length; i ++) {
					keywordArr[i] = cb.like(root.get("keyword").as(String.class), "%" +keywords[i].trim()+ "%");
					titleArr[i] = cb.like(root.get("title").as(String.class), "%" +keywords[i].trim()+ "%");
				}
				
				Predicate[] allArr = (Predicate[]) ArrayUtils.addAll(keywordArr, titleArr);
				query.where(cb.or(allArr));
				
				// 排序
				query.orderBy(cb.desc(root.get("publishTime").as(Date.class)));
				
				return query.getRestriction();
			}
		};
		List<News> list = newsRepository.findAll(spec);
		return buildPushNews(filterNews(list));
	}
	
	/**
	 * 过滤新闻, 先根据关键词黑名单过滤, 再根据相似度过滤
	 * @param newsList 新闻列表
	 * @return 过滤之后的新闻列表
	 */
	private List<News> filterNews(List<News> newsList) {
		NewsHandler handler = new NewsBlackKeywordHandler(ConstantUtil.NEWS_KEYWORD_BLACKLIST.split(","));
		handler.setNextHandler(new NewsSimilarDegreeHandler(ConstantUtil.NEWS_MAX_SIMILAR_DEGREE));
		return handler.filterNews(newsList);
	}
	
	@Override
	public Map<String, String> buildPushNews(List<News> newsList) {
		if(newsList != null && newsList.size() > 4) {
			Map<String, String> pushMsg = new HashMap<String, String>();
			
			// 使用第一条新闻title作为推送的title
			News firstNews = newsList.get(0);
			pushMsg.put("title", firstNews.getTitle());
			
			/* 新闻图片使用规则:
			 * 	如果thumbNail不为空,使用thumbNail作为新闻图片
			 * 	如果thumbNail为空, 使用此新闻页面的第一张图片作为新闻图片
			 * 	如果thumbNail为空并且新闻页面中没有图片, 使用默认图片
			 */
			String firstImgUrl = ConstantUtil.NEWS_FIRST_DEFAULT_IMG;
			if(!StringUtil.isNullStr(firstNews.getThumbnail())) {
				firstImgUrl = firstNews.getThumbnail();
			} else {
				List<Img> firstImgList = imgRepository.findByNewsIdOrderByInsertTimeAsc(firstNews.getId());
				if(firstImgList != null && firstImgList.size() > 0) {
					firstImgUrl = firstImgList.get(0).getNewImgUrl();
				}
			}
			
			StringBuffer content = new StringBuffer();
			content.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
				   .append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n ")
				   .append("<head> \n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> \n ")
				   .append("<meta http-equiv=\"Cache-Control\" content=\"public\" /> \n ")
				   .append("<title></title> \n <link rel=\"shortcut icon\" href=\"&lt;@path file='/asset/common/images/favicon.png'/&gt;\" /> \n ")
				   .append("<style>\n\t* {\n\t\tmargin:0;\n\t\tpadding:0;\n\t\tlist-style-type:none;\n\t\tfont-family:\"微软雅黑\",Helvetica, sans-serif;\n\t\tfont-size:100%;\n\t}\n\tbody {\n\t\tbackground-color:#efeff4;\n\t}\n\ta, area {\n\t\tblr:expression(this.onFocus=this.blur()); ")
				   .append("\n\t\ttext-decoration:none;\n\t\tcolor:#000;\n\t\tfont-size:1em;\n\t} \n\ta:focus {\n\t\toutline: none;\n\t} ")
				   .append("\n\t.i_xin_content{\n\t\twidth:99%;\n\t\tborder:1px solid ")
				   .append("#E4E4E4;\n\t\tborder-radius:0.5em;\n\t\toverflow:hidden;\n\t\tbackground-color:#FFF;\n\t\tpadding-bottom:1em;\n\t}\n\t.content_head{\n\t\theight:9em;")
				   .append("\n\t\tpadding-top: 0.5em;\n\t\twidth:95%;\n\t\tmargin:auto;\n\t\tposition:relative;")
				   .append("\n\t\toverflow:hidden;\n\t}\n\t.content_head img{\n\t\twidth:100%;\n\t}\n\t.content_head_title{\n\t\tcolor:#FFF;\n\t\tpadding:0.5em;")
				   .append("\n\t\twidth:100%;\n\t\tposition:absolute;\n\t\tbottom:0px;\n\t\tbackground:#000;\n\t\tfilter:alpha(opacity=75);-moz-opacity:0.75;-khtml-opacity: 0.75;opacity: 0.75;\"\n\t}\n")
				   .append("\t.content_body {\n\t\tfont-size:1em; \n\t\tline-height:1.5em;\n\t\tcolor:#000;\n\t\tpadding:0.5em;\n\t\ttext-align:justify;\n\t}\n")
				   .append("\t.content_list{\n\t\tpadding-top: 0.5em;\n\t}\n\t.content_list li{\n\t\tdisplay:block;\n\t\theight:3em; \n")
				   .append("\t\tborder-top:1px solid #d9d9d9;\n\t\twidth:100%;\n\t}\n\t.content_list a{\n\t\tposition:relative;\n\t\tdisplay:block; \n\t\twidth:93%;\n\t\tmargin:auto;\n\t}\n")
				   .append("\t.content_list img{\n\t\tfloat:right; \n\t\ttop:0.2em; \n\t\twidth:3em;\n \t\theight:3em;\n\t\tposition:relative; \n\t\toverflow:hidden;\n\t}\n")
				   .append("\t.content_list_item {\n\t\tfont-size:0.9em; \n\t\theight:4em;\n\t\tline-height:1.5em;\n\t\tpadding-right:0.5em;\n\t\tcolor:#000;\n")
				   .append("\t\tdisplay:table-cell;\n\t\tvertical-align:middle; \n\t\ttext-align:justify;\n\t\tposition:relative;\n\t}\n</style>")
				   .append(" \n </head> \n ")
				   .append("<body> \n <div class=\"i_xin_content\"> \n ")
				   .append(" <div class=\"content_head\"> \n ")
				   .append("<a href=\""+firstNews.getPageUrl()+"\"> \n")
				   .append("<div class=\"content_head_title\">\n "+firstNews.getTitle()+"\n </div>")
				   .append("<img src=\""+firstImgUrl+"\" onerror=\"this.src='"+ConstantUtil.NEWS_FIRST_DEFAULT_IMG+"'\" /> </a> \n </div> \n ")
				   .append("<div id=\"news_content\" class=\"content_list\">\n");
			
			
			// 从第二条新闻开始循环
			for(int i = 1; i < 5; i++) {
				News news = newsList.get(i);
				String imgUrl = ConstantUtil.NEWS_DEFAULT_IMG;
				if(!StringUtil.isNullStr(news.getThumbnail())) {
					imgUrl = news.getThumbnail();
				} else {
					List<Img> imgList = imgRepository.findByNewsIdOrderByInsertTimeAsc(news.getId());
					if(imgList != null && imgList.size() > 0) {
						imgUrl = imgList.get(0).getNewImgUrl();
					}
				}
				
				content.append("<div id=\"news_content"+i+"\" class=\"content_list\"> \n")
				   	   .append("<ul> \n")
				       .append("<li> <a href=\""+news.getPageUrl()+"\">")
				       .append("<img src=\""+imgUrl+"\" onerror=\"this.src='"+ConstantUtil.NEWS_DEFAULT_IMG+"'\" />")
				       .append("<span class=\"content_list_item\">"+news.getTitle()+"</span> </a> </li> \n </ul> \n </div>\n");
			}
			
			content.append("</div> \n </div> \n </body>\n</html>");
			
			pushMsg.put("content", content.toString());
			return pushMsg;
		} else {
			log.warn("新闻数量没有达到5条, 不推送!");
			return null;
		}
	}

	@Override
	public void deleteByIsEnabledFalse() {
		List<News> list = newsRepository.findByIsEnabledFalse();
		if(list != null && list.size() > 0) {
			log.info("需要删除" + list.size() + "条抓取失败的新闻");
			newsRepository.deleteInBatch(list);
			
			// 删除缓存中的数据
			for(News news : list) {
				String pageUrl = news.getPageUrl();
				String title = news.getTitle();
				if(!StringUtil.isEmpty(pageUrl)|| !StringUtil.isEmpty(title)) {
					CrawlerEHCacheUtil.removeCached(news.getChannelId(), pageUrl, title);
				}
			}
		}
	}

	@Override
	public void deleteNews(List<News> newsList) {
		if(newsList != null && newsList.size() > 0) {
			newsRepository.deleteInBatch(newsList);
			
			// 删除缓存中的数据
			for(News news : newsList) {
				String pageUrl = news.getPageUrl();
				String title = news.getTitle();
				if(!StringUtil.isEmpty(pageUrl)|| !StringUtil.isEmpty(title)) {
					CrawlerEHCacheUtil.removeCached(news.getChannelId(), pageUrl, title);
				}
			}
		}
	}
}
